home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / std / c / 390 < prev    next >
Encoding:
Text File  |  1996-08-06  |  7.2 KB  |  189 lines

  1. Path: solon.com!not-for-mail
  2. From: Chris Torek <torek@elf.bsdi.com>
  3. Newsgroups: comp.std.c,comp.lang.c.moderated
  4. Subject: Re: Integral promotion.
  5. Date: 14 Feb 1996 21:08:21 -0600
  6. Organization: Berkeley Software Design, Inc.
  7. Sender: clc@solutions.solon.com
  8. Approved: clc@solutions.solon.com
  9. Message-ID: <4fu835$9de@solutions.solon.com>
  10. References: <4fstj7$2l6@solutions.solon.com>
  11. Reply-To: torek@bsdi.com
  12. NNTP-Posting-Host: solutions.solon.com
  13.  
  14. (I sent this as a reply by mail but am also posting it.)
  15.  
  16. In article <4fstj7$2l6@solutions.solon.com> you write:
  17. >I got a problem understanding the rules on integral promotion in 
  18. >the C language ...
  19.  
  20. The rules in C are a little over-complicated (they were once entirely
  21. straightforward) but the complications mostly involve `unsigned'.
  22.  
  23. It seems a little odd that you mention `Visual C++ 4.0', which
  24. sounds like a C++ compiler; C++ is an entirely different language
  25. with entirely different subtleties.  (For instance, (sizeof 'x')
  26. is always 1 in C++ but is typically 2 or 4 in C.)
  27.  
  28. >Consider the following function:
  29. >
  30. >short test(short x1, short x2);
  31.  
  32. The prototype is important.
  33.  
  34. We can mostly ignore the (valid) main() you provided...
  35.  
  36. >short test(short x1, short x2)
  37. >{
  38. >  short result;
  39. >  result = x1 + x2;  /* Warning:  '=' : conversion from 'int '
  40. >                         to 'short ', possible loss of data */
  41. >  return result;
  42. >}
  43.  
  44. Again, we have a prototype-style definition, so the declaration
  45. and definition match.
  46.  
  47. `Old' (pre-ANSI) C always performed `the integral promotions' on
  48. all parameters.  For this reason, ANSI/ISO C does the same with
  49. parameters for which a prototype is not available -- this covers
  50. both `functions without prototypes' and `functions whose prototype
  51. ends in ", ..."', e.g., printf().  (This means that, for instance,
  52. you can never pass a short to printf().  By the time printf()
  53. receives it, the value has been converted to an int.)  In Old C,
  54. if you defined a function like this:
  55.  
  56.     void func(i, s, c)
  57.         int i;
  58.         short s;
  59.         char c;
  60.     {
  61.     }
  62.  
  63. the compiler would take the three `int's that it knew had been
  64. passed (due to the integral promotions), put the first in `i',
  65. narrow the second to `short' and put it in `s', and narrow the
  66. third and put it in `c'.  This promotion and narrowing sometimes
  67. needs extra instructions (it depends on the machine, and maybe
  68. the type as well).
  69.  
  70. For efficiency and/or less-surprising behavior, ANSI C says that
  71. prototyped parameters (as you have here) are not promoted.  (Many
  72. compilers simply promote them at the caller, and then narrow them
  73. before entering the function, just as for Old C, but they can now
  74. avoid these steps in this case.)
  75.  
  76. Because ANSI C requires promotion for non-prototypes and non-promotion
  77. for prototypes, it is important to make sure you never mix up the
  78. two kinds of declarations and definitions when you use `narrow'
  79. types (char, short, signed char, unsigned char, unsigned short,
  80. and float).
  81.  
  82. Anyway, with all that out of the way, we have a correct declaration
  83. and definition for function test(), with `x1', `x2', and `result' all
  84. being `short'.
  85.  
  86. Now, the last hard part is the line:
  87.  
  88.     result = x1 + x2;
  89.  
  90. This takes the values in x1 and x2 (both of type (signed) short
  91. and hence in the range [SHRT_MIN .. SHRT_MAX]), performs the
  92. integral promotions (yeilding values of type (signed) int but still
  93. in the range [SHRT_MIN .. SHRT_MAX]), and adds them.  At this
  94. point, the sum still has type (signed) int and could potentially
  95. be in the range [2*SHRT_MIN .. 2*SHRT_MAX].
  96.  
  97. Now, if `int' and `short' are the same width, a result that would
  98. be outside the range [SHRT_MIN .. SHRT_MAX] is also outside the
  99. range [INT_MIN .. INT_MAX], and the behavior is undefined (ANSI C
  100. section 3.3, p. 39, ll. 15--17).
  101.  
  102. On the other hand, if `int's are wider than `short's, we are pretty
  103. safe (maybe not completely safe, but pretty safe anyway) in assuming
  104. that INT_MIN <= 2*SHRT_MIN and INT_MAX >= 2*SHRT_MAX.  Most likely
  105. INT_MIN and INT_MAX are something like 65536 times bigger.  Either
  106. way, the sum is not outside the representable range and the undefined
  107. clause in 3.3 does not apply.
  108.  
  109. The final step is to convert the sum back to type `short'.  Here
  110. things get a little fuzzy; comp.std.c has had arguments as to what
  111. happens if the sum is `int', `int' is wider than `short', and the
  112. sum is not representable as a `short'.  The Standard distinguishes
  113. between `implicit' and `explicit' conversions, and this is an
  114. `implicit' conversion (casts are the usual, perhaps the only, way
  115. to write explicit conversions).  Some have argued that an explicit
  116. conversion should never cause an overflow, but there seems to be
  117. no wording in the standard to justify this position.  There appears
  118. to be agreement that implicit conversion might overflow.  The
  119. behavior on overflow would be undefined.
  120.  
  121. Your particular compiler is likely to ignore the overflow and
  122. truncate the result (most compilers do, and the warning -- `possible
  123. loss of data' -- suggests that yours does).
  124.  
  125. It is interesting, however, to note that if overflow will ever occur,
  126. it will occur no matter whether the ranges of short and int coincide
  127. or not.  The only thing that changes here is the exact operation that
  128. overflows: the sum, or the implicit conversion in the assignment.
  129.  
  130. The compiler's warning suggests (but by no means guarantees) that
  131. this particular compiler has wider `int's than `short's.  Since this
  132. is comp.lang.c.moderated (as well as comp.std.c), it is reasonable
  133. to ask whether the warning the compiler emits is `reasonable'.  I
  134. think not, but this is purely my opinion.  The Standard itself allows
  135. arbitrary warnings, so it is no help here.
  136.  
  137. >my compiler (Microsoft Visual C++ 4.0), automagically converts 
  138. >my short-parameters to int's, even though all variables involved 
  139. >are short. I know that the standard says that all arguments can 
  140. >be converted to the biggest 'type' of all the arguments,
  141.  
  142. See the detailed explanation above :-)
  143.  
  144. >but is it correct that char's and short's always are promoted to int's, 
  145. >without regard to the other arguments in the expression?
  146.  
  147. In expressions, yes.
  148.  
  149. >The reason I ask is that I am writing code that should be 
  150. >portable from a 16-bits environment (where short and int are the 
  151. >same size) to a 32-bits environment. (My 16-bits compiler does 
  152. >not complain about the code).
  153.  
  154. Perhaps it should, since the sum itself can overflow here just as
  155. easily as a conversion from a 32-bit-int sum to a 16-bit short
  156. `result'.  Again, the Standard is no help here, and my personal
  157. opinion is that a warning is unwarranted (because such a warning
  158. should then be given for virtually every operator).
  159.  
  160. I would find an `assignment may truncate' warning appropriate, and
  161. maybe even helpful, when the target of a complex expression has a
  162. narrower type than at least one of the operands *before* promotion.
  163. That is, if someone writes:
  164.  
  165.     short a, b, prod;
  166.  
  167.     prod = a * b;
  168.  
  169. I would object (mildly) to a warning, but if they had:
  170.  
  171.     int a;
  172.     short b, prod;
  173.  
  174.     prod = a * b;
  175.  
  176. I would not.  In this case, I think an explicit truncation somewhere
  177. should eliminate the warning, so that either:
  178.  
  179.     prod = (short)(a * b);
  180.  
  181. or:
  182.  
  183.     prod = (short)a * b;    /* not the same thing at all! */
  184.  
  185. should not warn.  But, again, this is simply my opinion.
  186. -- 
  187. In-Real-Life: Chris Torek, Berkeley Software Design Inc
  188. El Cerrito, CA    Domain:    torek@bsdi.com    +1 510 234 3167
  189.